#ifndef	_COMMON_H
#define	_COMMON_H

#define	_GNU_SOURCE
#include <sys/mman.h>
#include <dlfcn.h>

#include <stdio.h>
#include <stdlib.h>

#ifdef	DEBUG
#  define	pdbg(fmt, x...)	fprintf(stdout, "<LIBA>: <%s>[%d]: " fmt, __FILE__, __LINE__, ##x)
#else
#  define	pdbg(fmt, x...)
#endif

#define	plog(fmt, x...)	fprintf(stdout, fmt, ##x)
#define	perr(fmt, x...)	fprintf(stderr, "<LIBA>" fmt, ##x)

typedef enum func_index {
	LIBC_MALLOC = 0,
	LIBC_CALLOC,
	LIBC_REALLOC,
	LIBC_FREE,
	LIBC_MMAP,
	LIBC_MREMAP,
	LIBC_MUNMAP,
	LIBC_POSIX_MEMALIGN,
	LIBC_MEMALIGN,
	LIBC_FUNCS_MAX
} t_func_index;

typedef	const char t_func_name[32];

typedef	struct libc_func {
	t_func_index		index;
	t_func_name		name;
	void			*handle;
} t_libc_func;

extern t_libc_func libc_funcs[];
extern int init_libc_handles(void);

static inline void *get_libc_handle(t_func_index index)
{
	return libc_funcs[index].handle;
}

/* wrapper of libc functions used internally */
static inline void* libc_malloc(size_t size)
{
	void *(*p)(size_t) = get_libc_handle(LIBC_MALLOC);
	return p(size);
}

static inline void* libc_calloc(size_t nmemb, size_t size)
{
	void *(*p)(size_t, size_t) = get_libc_handle(LIBC_CALLOC);
	return p(nmemb, size);
}

static inline void* libc_realloc(void *ptr, size_t size)
{
	void *(*p)(void *, size_t) = get_libc_handle(LIBC_REALLOC);
	return p(ptr, size);
}

static inline void libc_free(void *ptr)
{
	void *(*p)(void *) = get_libc_handle(LIBC_FREE);
	p(ptr);
}

static inline void *libc_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
{
	void *(*p)(void*, size_t, int, int, int, off_t) = get_libc_handle(LIBC_MMAP);
	return p(addr, length, prot, flags, fd, offset);
}

static inline void* libc_mremap(void *old_address, size_t old_size, size_t new_size, int flags, ...)
{
	void *(*p)(void*, size_t, size_t, int) = get_libc_handle(LIBC_MREMAP);
	return p(old_address, old_size, new_size, flags);
}

static inline int libc_munmap(void *addr, size_t length)
{
	int (*p)(void *, size_t) = get_libc_handle(LIBC_MUNMAP);
	return p(addr, length);
}

static inline int libc_posix_memalign(void **memptr, size_t alignment, size_t size)
{
	int (*p)(void **, size_t, size_t) = get_libc_handle(LIBC_POSIX_MEMALIGN);
	return p(memptr, alignment, size);
}

static inline void *libc_memalign(size_t boundary, size_t size)
{
	void *(*p)(size_t, size_t) = get_libc_handle(LIBC_MEMALIGN);
	return p(boundary, size);
}

#endif
